/*
 * $Id: IPTDSClient.java,v 1.18 2011/07/07 13:30:49 navdeep_mahajan Exp $
 *
 * Copyright (C) 2006 General Electric Company. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 *
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */

package com.ge.healthcare.autosc.processor.iptds.worker;

import java.net.Socket;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.commands.FTPCommand;
import com.ge.healthcare.autosc.common.commands.IFTCommand;
import com.ge.healthcare.autosc.common.database.pojo.HstSystemContactInfo;
import com.ge.healthcare.autosc.common.dbLogger.ContactLogger;
import com.ge.healthcare.autosc.common.exceptions.BasicException;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.GSCCUtil;
import com.ge.healthcare.autosc.processor.IPTDSCodeConstants;
import com.ge.healthcare.autosc.processor.exception.IPTDSException;
import com.ge.healthcare.autosc.processor.iptds.IPTDSProperties;
import com.ge.healthcare.autosc.processor.iptds.IPTDSSocketHandler;
import com.ge.healthcare.autosc.processor.iptds.IPTDSSystemInfo;
import com.ge.healthcare.autosc.processor.iptds.transaction.IPTDSFTPCommand;
import com.ge.healthcare.autosc.processor.iptds.transaction.TransactionController;

/**
 * 
 * @author 212042946
 * 
 * 
 */
public class IPTDSClient implements IPTDSWorker {

	private Socket socket = null;
	private IPTDSThreadPoolManager threadPoolManager = null;
	private volatile boolean stop = false;
	private IPTDSSocketHandler socketHandler = null;
	private IPTDSSystemInfo systemInfo = null;
	private IPTDSFTPCommand iptdsFTPCommand = null;
	private String ipAddress = null;

	public IPTDSClient(Socket socket, IPTDSThreadPoolManager threadPoolManager,
			IPTDSProperties iptdsProperties) {
		this.threadPoolManager = threadPoolManager;
		this.socket = socket;
	}

	public void run() {
		boolean status = true;
		String comment = "AOK - IPTDS Success";

		ipAddress = socket.getInetAddress().getHostAddress();
		ContactLogger.startLogging(ipAddress,
				HstSystemContactInfo.IPTDS_CONNECT_TYPE);
		ContactLogger.addContactLog("Worker begins IPTDS conversation");

		try {
			socketHandler = new IPTDSSocketHandler(socket);
			socketHandler.open();

			handShake();

			ftpLogin();

			performFtpTransaction();

		} catch (IPTDSException iptdse) {
			status = false;
			comment = "Exception - " + iptdse.getMessage() + ", code: "
					+ iptdse.getErrorCode();
		} catch (RuntimeException re) {
			status = false;
			comment = "Exception - " + re.getMessage() + ", code: Runtime";
			ASCLogger.fatal(this.getClass(), "startTransaction",
					"IPTDSClient worker got runtime exception.", re);
			//We shouldn't be throwing Runtime exceptions as it kills the Threadpool and shuts down IPTDS processing
			//throw re;
		} finally {
			logoutTransaction(status);

			// log Chronology
			logChronology(comment, status);
			threadPoolManager.removeWorker(this);
			ContactLogger.endLogging(null);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ge.healthcare.autosc.processor.iptds.worker.IPTDSWorker#stop()
	 */
	public void stop() {
		this.stop = true;
	}

	/**
	 * Tells if stop event has been initiated or not
	 * 
	 * @return
	 */
	public boolean isStopped() {
		return this.stop;
	}

	public static String getProcessType() {
		return "IPTDS";
	}

	private void handShake() throws IPTDSException {
		ContactLogger.addContactLog("Start handshaking");
		Handshake handshaker = new Handshake();
		systemInfo = handshaker.handShake(threadPoolManager, socketHandler);
		systemInfo.setSocketHandler(socketHandler);
		if (systemInfo.isSimulator() == true) {
			systemInfo.turnOnSimulator();
		}
		ContactLogger.addContactLog("End handshaking");
	}

	/*
	 * Given a list of systems based on the client IP address and usn, perform
	 * ftp login to the system. This is needs in order to transfer the chf file
	 * in the first place.
	 */
	private void ftpLogin() throws IPTDSException {
		String methodName = "ftpLogin";
		boolean successLogin = false;
		IFTCommand iFTCommand = null;
		iFTCommand = new FTPCommand();
		Properties logins = systemInfo.getLoginInfo();
		StringBuffer loginNames = new StringBuffer();
		ASCLogger.debug(this.getClass(), methodName, "There are "
				+ logins.size() + " login against system "
				+ systemInfo.getSystemId());

		ContactLogger.addContactLog("Start FTP Login");
		Enumeration<?> usernames = logins.propertyNames();
		while (usernames.hasMoreElements()) {
			String username = (String) usernames.nextElement();
			String passcode = (String) logins.getProperty(username);
			try {
				iFTCommand.login(systemInfo.getIpAddress(), username, passcode);
				iptdsFTPCommand = new IPTDSFTPCommand(iFTCommand);
				ASCLogger.info(String.format("Successfully login. System %s using login name %s", systemInfo.getSystemId(), username));
				successLogin = true;
				break;
			} catch (BasicException ftpe) {
				// Failed to login, try next login
				loginNames.append("{" + username + "}");
				continue;
			}
		}
		// All login attempts failed.
		if (successLogin == false) {
			String msg = "Failed ftp login to remote system "
					+ systemInfo.getSystemId()
					+ " with all available logins. Login names: "
					+ loginNames.toString();
			ContactLogger.addContactLog(msg);
			throw new IPTDSException(msg, IPTDSCodeConstants.PRC_F_TDS_006);
		} else {
			ContactLogger.addContactLog("FTP login succeeds");
		}
	}

	private void performFtpTransaction() throws IPTDSException {
		ContactLogger.addContactLog("Start IPTDS transaction");
		systemInfo.setFTPCommand(iptdsFTPCommand);
		TransactionController controller = (TransactionController) ApplicationInfo
				.getApplicationContext().getBean("transactionController");
		controller.startTransaction(socketHandler, systemInfo);
		ContactLogger.addContactLog("End IPTDS transcation");
	}

	private void logoutTransaction(boolean status) {
		String methodName = "logoutTransaction";
		String exitMessage = "  !!!! SUCCESS !!!!   ";
		if (status == false) {
			exitMessage = "   !!!! FAILED !!!!   ";
		}

		ASCLogger
				.info(this.getClass(), methodName,
						"Start logging out from all IPTDS communication (ftp and handshake socket)");
		if (iptdsFTPCommand != null) {
			iptdsFTPCommand.logout();
		}
		if (socketHandler != null) {
			ASCLogger.info(this.getClass(), methodName, "End session with: "
					+ socketHandler.getRemoteAddress() + exitMessage);
			socketHandler.close();
		}
	}

	/*
	 * Log the Chronology
	 */
	private void logChronology(String comment, boolean status) {
		HashMap<String, String> itemMap = new HashMap<String, String>();
		String systemId = ipAddress;
		String completionCode;
		if (systemInfo != null) {
			systemId = systemInfo.getSystemId();
		} else {
			ASCLogger.warn(this.getClass(), "logChronology",
					"Cannot add Chronology because we do not have system information for IP "
							+ ipAddress);
			return;
		}

		if (status == true) {
			completionCode = "Success";
		} else {
			completionCode = "Failure";
		}

		itemMap.put(GSCCUtil.SYSTEM_ID, systemId);
		itemMap.put(GSCCUtil.LOGIN_ID, "asc_acct");
		itemMap.put(GSCCUtil.RFS_NUMBER, "");
		itemMap.put(GSCCUtil.TYPE_ID, "Inbound");
		itemMap.put(GSCCUtil.PLATFORM, "AutoSC");
		itemMap.put(GSCCUtil.ATTEMPT_NUM, "1");
		itemMap.put(GSCCUtil.APPLICATION, getProcessType());
		itemMap.put(GSCCUtil.COMPLETION_CODE, completionCode);
		StringBuffer commentBuff = new StringBuffer();
		if (!ASCUtil.isNullOrEmpty(comment)) {
			commentBuff.append(comment);
		}
		if (!ASCUtil.isNullOrEmptyObject(iptdsFTPCommand)
				&& !ASCUtil.isNullOrEmptyList(iptdsFTPCommand
						.getRetrievedFiles())) {
			commentBuff.append(" ").append("Retrieved Files:");
			for (int iCount = 0; iCount < iptdsFTPCommand.getRetrievedFiles()
					.size(); iCount++) {
				if (iCount != 0) {
					commentBuff.append(",");
				}
				commentBuff.append(iptdsFTPCommand.getRetrievedFiles().get(
						iCount));
			}
		}
		itemMap.put(GSCCUtil.COMMENT, commentBuff.toString());
		GSCCUtil.addChronology(itemMap);
	}
}
